home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 March / EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso / earcd / editor / wordplay.lha / readme next >
Text File  |  1996-02-03  |  35KB  |  728 lines

  1. ------------------------------------------------------------------------------
  2.  
  3. Wordplay Version 7.20     Evans A Criswell   08-21-94
  4.  
  5. ------------------------------------------------------------------------------
  6.  
  7. This program was written for fun and is free.  Distribute it as you please,
  8. but please distribute the entire package, with the original words700.txt and 
  9. the readme file.  If you modify the code, please mention my name in it as the
  10. original author.  Please send me a copy of improvements you make, because I
  11. may include them in a future version.
  12.  
  13. I may be contacted by email at criswell@cs.uah.edu
  14.  
  15. Evans A Criswell
  16. Research Associate
  17. Computer Science Department
  18. University of Alabama in Huntsville
  19. Huntsville, AL  35899
  20.  
  21. ------------------------------------------------------------------------------
  22.  
  23. Wordplay is an anagram finder.  What is an anagram?  Well, let's turn to
  24. Merriam-Webster's Collegiate Dictionary, Tenth Edition:
  25.  
  26. Definition:  anagram:  a word or phrase made by transposing the letters
  27.                of another word or phrase.  
  28.  
  29. Each letter in the anagram must appear in the same frequency as in the 
  30. original string.
  31.  
  32. For example, the letters in the word "stop" can be rearranged to spell
  33. "tops" or "pots" or "sotp".  "sotp" is not a word and is not of interest
  34. when generating anagrams.  "stop" has four letters, so there are 24 ways
  35. to rearrange its letters.  However, very few of the rearrangements actually
  36. spell words.  
  37.  
  38. Wordplay, by using a list of words, takes a specified string of letters and 
  39. uses the list of words to find anagrams of the string.  
  40.  
  41. ------------------------------------------------------------------------------
  42.  
  43. Compiling the program:
  44.  
  45. Under UNIX, issue the command
  46.  
  47.   cc -o wordplay wordplay.c
  48.  
  49. This assumes your native "cc" is an ANSI compiler.  If not, it WILL NOT WORK.
  50. If you have gcc, use it instead:
  51.  
  52.   gcc -o wordplay wordplay.c
  53.  
  54. Feel free to use optimization options!  They do make a significant difference
  55. with some compilers.
  56.  
  57. -----------------------------------------------------------------------------
  58.  
  59. Usage:
  60.  
  61. IMPORTANT!!!!  The options have changed significantly since version 6.00!!!
  62.  
  63. To use the program, invoke the program with a combination of options that
  64. make sense.  Here is the format:
  65.  
  66.      wordplay "string to anagram" [-slxavnXmXdX] [-w word] [-f wordfile]
  67.  
  68. where the capital X's represent integers.  Please see the examples below
  69. the option descriptions.  The square brackets are not part of the command
  70. line.  They simply are used to show that the options they surround are
  71. optional.
  72.  
  73. wordplay:              This is the name of the executable (under UNIX) after 
  74.                you have compiled the program.  Under other 
  75.                environments, method of invocation may vary.
  76.  
  77. "string to anagram" :  This should be seen to the program AS A SINGLE
  78.                ARGUMENT.  If you feel you must put spaces in the
  79.                string, under UNIX, you will have to put backslashes
  80.                in front of the spaces or just put the entire string
  81.                in double quotes.  Just leave the spaces out because
  82.                the program throws them out anyway.  Under environments
  83.                other than UNIX, you may be forced to omit them anyway,
  84.                like under MS-DOS or OS/2 .
  85.  
  86. Other options:
  87.  
  88.   s:  Silent operation.  If this option is used, the header and line
  89.       numbers are not printed.  This is useful if you want the output to
  90.       contain only the anagrams.  Use this option with the l (and x) option
  91.       to generate a wordlist which can be piped or redirected.
  92.       
  93.       This option does not suppress error messages that are printed to 
  94.       stderr.  Finding zero anagrams is not considered an error, unless it
  95.       is due to some other abnormal error.
  96.  
  97.   l:  Print list of candidate words before anagramming.  This is the list of 
  98.       words that can be spelled with the letters from the specified string,
  99.       with no letters being used more often that they appear in the input
  100.       string.
  101.  
  102.   x:  Do not perform anagramming.  Use with l if you just want the 
  103.       candidate word list without anagrams.
  104.  
  105.   a:  Allow anagrams containing two or more occurrences of a word.
  106.  
  107.   v:  Consider strings with no vowels as candidate words and do not give
  108.       up when there are no vowels remaining after extractions.
  109.  
  110.   m:  Limit candidate word length to a maximum number of letters.
  111.       Follow by an integer.  m12 means limit words to 12 letters.
  112.       m5 means limit them to 5 letters.
  113.  
  114.   n:  Limit candidate word length to a minimum number of letters.
  115.       Follow by an integer.  n2 means limit words to 2 letters.
  116.       n11 means limit them to 11 letters.
  117.  
  118.   d:  Limit number of words in anagrams to a maximum number.  
  119.       Follow by an integer.  d3 means no anagrams should contain more
  120.       than 3 words.  d12 means limit anagrams to 12 words.
  121.  
  122.   w:  Specify a word which should appear in all anagrams.  This is useful
  123.       if you already have a word in mind that you want in the anagrams. 
  124.       This option should be specified at the end of the command, followed
  125.       by a space and the word to use.
  126.  
  127.   f:  Specify which word list to use.  See example!  This option should
  128.       be specified at the end of the command, followed by a space and the 
  129.       alternate wordfile name.  This is useful if you have other word lists
  130.       to try or if you are interested in making your own customized word
  131.       list.
  132.  
  133.       New feature:  Use a hyphen as the filename if the wordlist should
  134.       be read from stdin. 
  135.  
  136.  
  137. Examples of usage:
  138.  
  139. (1)
  140. wordplay persiangulf 
  141.  
  142.      Anagram the string "persiangulf" .
  143.  
  144. (2)
  145. wordplay anagramming -lx
  146.  
  147.      Print the list of words from the wordlist that can be spelled by using
  148.      the letters from the word "anagramming".  A letter may not be used more
  149.      often than the number of times it occurs in the word "anagramming".
  150.      No anagrams are generated. 
  151.  
  152. (3)
  153. wordplay tomservocrow -n3m8
  154.  
  155.      Anagram the string "tomservocrow" .  Do not use words shorter than 
  156.      3 letters or longer than 8 letters.
  157.  
  158. (4)
  159. wordplay persiangulf -ld3m10 -f /usr/dict/words
  160.  
  161.      Print the candidate words for the string "persiangulf".
  162.      Print anagrams containing up to 3 words, without considering any
  163.      words longer than 10 characters.  Usr the file "/usr/dict/words"
  164.      rather than "words700.txt".
  165.  
  166. (5)
  167. wordplay soylentgreen -n3w stolen -f w2     or
  168. wordplay soylentgreen -n3 -w stolen -f w2   or
  169. wordplay soylentgreen -n3f w2 -w stolen     or
  170. wordplay soylentgreen -n3 -f w2 -w stolen   (get the idea?)
  171.  
  172.      Print anagrams of "soylentgreen" containing the word "stolen" and
  173.      use the file "w2" as the wordlist file.  Discard candidate words 
  174.      shorter than 3 characters.
  175.  
  176. (6)
  177. wordplay university -slx
  178.  
  179.      Print the candidate word list for the string "university".  The
  180.      output will consist of just the words.  This output is more useful
  181.      for redirecting to a file or for piping to another program.
  182.  
  183. (7)
  184. wordplay trymeout -s
  185.  
  186.     Anagram the string "trymeout" and print the anagrams with no line
  187.     numbers.  The header will not be printed.  This is useful for piping
  188.     the output to another process (or saving it to a file to be used
  189.     by another program) without having to parse the output to remove the
  190.     numbers and header.
  191.  
  192. (8)
  193. wordplay trymeout -v
  194.  
  195.     Anagram "trymeout" as usual, but in case vowel-free strings are in
  196.     the wordlist, consider them as possible candidate words.
  197.  
  198. (9)
  199. cat wordlist1 wordlist2 wordlist3 | sort -u | wordplay trymeout -f -
  200.  
  201.     Anagram "trymeout" and read the wordlist from stdin, so that, in
  202.     this case, under UNIX, the three wordlists "wordlist1", "wordlist2",
  203.     and "wordlist3" will be concatenated and piped into wordplay as
  204.     the wordlist.  The "sort -u" is there to remove duplicate words
  205.     from the combined wordlist.
  206.  
  207. ------------------------------------------------------------------------------
  208.  
  209. Using the "f" option to specify a word file:
  210.  
  211. If the option specifiers are combined, as in "an7m7d5f" or "d3n5f", the f 
  212. should come last, followed by a space and the word list file, as shown in 
  213. example number 4 above.
  214.  
  215. The "w" option is used in the same manner.
  216.  
  217. ------------------------------------------------------------------------------
  218.  
  219. Notes:
  220.  
  221. Limit the number of words to consider, if desired, using the n and m options,
  222. as well as the d option, to limit depth, when anagramming certain 
  223. time-consuming strings.
  224.  
  225. Plurals and past tenses
  226.  
  227. The words700.txt does not contain plural forms of nouns obtained by adding "s"
  228. or "es".  It usually does not contain verb forms obtained by adding "ed" or
  229. "d", and it does not contain many adjective forms obtained by adding "y".
  230. If the string you are anagramming contains an "s", try anagramming the 
  231. string without the "s" and add an "s" in the output.  This trick may also
  232. work effectively with "d" and "y".
  233.  
  234. Apostrophes, hyphens, and other non-alphabetics
  235.  
  236. All non-alphabetic characters in a word are preserved, INCLUDING BLANKS!!!
  237. If you have a dictionary with words like "DON'T", "ONE-EYED", and
  238. "ATOMIC NUMBER", each will be correctly processed.  Note that words
  239. like "ATOMIC NUMBER" or "KNOW IT ALL" in your word list will be considered
  240. as a single word by the program!  If "ATOMIC" and "NUMBER" appear as 
  241. single words also, "ATOMIC NUMBER" will appear twice in the output, once
  242. as a one-word anagram and once as a two-word anagram.  This is not a flaw
  243. in the program.  The words700.txt word list does not contain "double words",
  244. but other dictionaries, like web2/web2a, do contain such things.
  245.  
  246. The "words700.txt" wordfile:
  247.  
  248. If no wordfile is specified, "words700.txt" is used.  It is highly 
  249. recommended that the "words700.txt" file distributed with the program be 
  250. used, since many nonsense two and three-letter combinations that are not 
  251. words have been eliminated.  This makes the quality of the output slightly 
  252. better and speeds execution of the program a slight bit.  Any word list may 
  253. be used, as long as there is one word per line.  Feel free to create your 
  254. own custom word list and use it instead.
  255.  
  256. ------------------------------------------------------------------------------
  257. Program development history:
  258.  
  259. Version 1.00
  260.  
  261. In a restroom in the building I used to work, in the last stall, one person
  262. had written "roll tide" and someone else had responded with "war eagle".
  263. (University of Alabama and University of Auburn rivalry).  Well, some people
  264. started rearranging the letters of the two scribbles and soon there was quite
  265. a long column of anagrams below each.  People then wrote other things, like
  266. "tomatoes" and "batse/gro" (a project name) and "boredom" and those got
  267. anagrammed by everyone.  I thought as I sat there one day, "How hard would it
  268. be to write a program to do that?".  One night, I decided to give it a try.
  269. On March 29, 1991, I tackled it.  I coded one little step at a time and got
  270. it working.  I anagrammed "Persian Gulf" and wrote 30 anagrams of it in that
  271. restroom stall.  I took the code and tried to run it under VMS.  I had to 
  272. change one line to make it work.  It also worked on a Stardent UNIX computer 
  273. after changing that same line.  
  274.  
  275. Version 1.00 was written in one night.  I sat down at my PC at home, and using
  276. Microsoft Fortran 5.0, coded it up.  It just read the words into an array, and 
  277. prompted for strings to anagram or a "." to terminate.  Each time a string was
  278. entered, the number of occurrences of each letter was counted and stored in
  279. an array of size 26.  The word list was then filtered, with the result going
  280. to a second array.  Any word containing a letter that did not appear in the
  281. string being anagrammed was not copied over.  This eliminated many of the
  282. words in most cases.  
  283.  
  284. Lengths of the words were checked while doing one and two word anagrams.
  285. If the length of a word was not equal to the length of the string being 
  286. anagrammed, there was no need to check it.  Likewise, with two-word anagrams,
  287. there was no need to check them if the sums of the lengths did not match the
  288. length of the string being anagrammed.
  289.  
  290. This program did the job, but was quite slow.  
  291.  
  292. Version 1.10
  293.  
  294. I had two main thoughts in my head when I worked on the program again five
  295. days later on April 3, 1991.  First, it needed to run faster, and second,
  296. if the program takes a while to execute, it would be nice if it could give 
  297. an indication of how long the anagramming would take.
  298.  
  299. The speed increase came from inserting a second word filter into the program.
  300. Basically, if a word contains more of a particular letter than the string
  301. being anagrammed, it can be tossed out.  Note than any word thrown out in
  302. pass one would be thrown out by this second pass, but since the second pass
  303. is much more "expensive" timewise to execute, the first pass was not changed.
  304. The second pass simply works on the list produced by pass one, and eliminates,
  305. on the average, one third to one half of the words remaining after pass one.
  306. The result of pass two is the list of words that can be spelled using the 
  307. letters of the string being anagrammed.  The result of the second word filter
  308. being inserted was an average three to four times faster execution.
  309.  
  310. I inserted a loop similar to the loop the program executes while anagramming 
  311. and timed it when the program started to see how long it took on whatever 
  312. machine it was being run on to get a rough idea of the machine speed.  This 
  313. number, whatever it was, was multiplied by the square of the number of 
  314. candidate words remaining after filtering in order to produce an estimate.  
  315. This speed test only worked under OS/2, though.  Running the program on any 
  316. other system required commenting out that code.  
  317.  
  318. A small but nice thing I did is automatically converted all letters in the
  319. user's input strings to upper case so the user wouldn't be forced to use all
  320. upper case.  Blanks were automatically stripped out of the user's string so
  321. that they wouldn't have to run things together.
  322.  
  323. I decided at this point to go ahead and give the user some commands that
  324. could be run by using a "/" as the first character of the string.  Most were
  325. trivial to implement.  "VER" (print program version), "WORD" (print last
  326. string anagrammed), "HELP" (print commands available), "EXIT" (same as 
  327. entering a "." as the first character), and "LIST".  "LIST" printed the
  328. candidate word list after pass two.  Remember in elementary school where
  329. the teacher would write a word on the board and ask you to find as many
  330. words as you could that could be spelled using the word's letters?  That's
  331. what the "LIST" command provided.
  332.  
  333. Version 1.11
  334.  
  335. This version was written on April 11, 1991.
  336.  
  337. An array containing the lengths of the filtered words was added to the
  338. program to keep from having to repeatedly recompute lengths of the words.
  339. The length-based optimizations from version 1.00 ran faster this way.
  340.  
  341. Version 2.00
  342.  
  343. One day after writing version 1.11, on April 12, 1991, I made a major
  344. upgrade.  I decided the program was running efficiently enough to 
  345. possibly handle three word anagrams.  I coded the three word anagram
  346. using basically the same technique I used for the two-word ones.  I
  347. checked to see if the lengths of the three words being tested added to
  348. the length of the string being anagrammed before testing the number of 
  349. letters, etc.  
  350.  
  351. More commands and options were added in this version.  Since three word
  352. anagrams took a lot of time and produced lengthy output, I figured most
  353. of the time, users would not want to do the three word anagramming.  I
  354. decided to make various steps of the program options that could be enabled
  355. or disabled.  The steps that could be disabled or enabled independently
  356. were the pass two word filter, one word anagrams, two word anagrams, and
  357. three word anagrams.  The three word anagrams were disabled by default.
  358. The new commands were "DIS" and "ENA" to enable and disable options, along
  359. with "STAT", which showed the settings of various options.
  360.  
  361. Version 2.10
  362.  
  363. Only four days after writing version 2.00, I improved the program again
  364. (4-14-91) by adding the minimum and maximum candidate word length restriction
  365. options.  This allowed the user to specify that words shorter or longer than 
  366. a specified number of letters should not be considered as candidate words.
  367. This weeds out a lot of small words and speeds things up when anagramming 
  368. longer strings.  Obviously, if the length of the string being anagrammed
  369. is less than twice the minimum word length, no two word anagrams will be
  370. found.  Also, if the length of the string being anagrammed is more than 
  371. twice the the minimum word length, no two word anagrams will be found, so
  372. those checks were put in.  Similar checks were put in for the three word
  373. anagrams.  The "MIN" and "MAX" commands were the new commands added to
  374. allow the user specify minimum and maximum word lengths.
  375.  
  376. Version 3.00
  377.  
  378. The version 2.10 was satisfactory for quite a while (8 months).  
  379. On December 16, 1991, however, I upgraded it again, and to users of the
  380. program, except for the new version number "3.00", the program looked
  381. and behaved exactly the same way, except for a couple of things.  The output
  382. was no longer in alphabetical order and the program was faster, especially
  383. when doing three word anagrams.  Losing alphabetical order was a small
  384. price to pay for the substantial speed increase, which was accomplished by
  385. sorting the word list by word length, using the combsort algorithm from BYTE 
  386. magazine.  A reprint of the article describing combsort appears in the book 
  387. THE BEST OF BYTE.  Anyway, this was the second optimization.  Let me first
  388. describe the first optimization:
  389.  
  390. After reading the user's string to anagram, the least and greatest letters
  391. occurring in the string were stored.  For example, if the user entered
  392. "boredom", then the minimum letter was 2 for "b" and the maximum was 
  393. 18 for "r".  The array containing the numbers of occurrences for each letter
  394. of the alphabet was then processed into two "parallel arrays", called 
  395. nlasci and nlascl.  The arrays were created in such a way that, for example,
  396. if nlasci(1) was 4 and nlascl was 1, this meant there were was one "d" in the
  397. input string.  nlasci(2) being 9 and nlascl(2) being 4 would mean there were 4
  398. occurrences of "i" in the input string.  Note that the sizes of nlasci and
  399. nlascl never exceed 26 and the number of elements used in each is equal to
  400. the number of distinct letters in the input string.  For example, "boredom"
  401. contains 6 distinct characters, so the nlasci and nlascl arrays only contain
  402. 6 entries each, with the empty slots in nlasci being filled with "-1" and
  403. empty slots in nlascl being filled with "0" .  Why create these arrays?
  404. Because the anagram procedure now has a list of which letters to check for
  405. equal numbers of occurrences instead of having to check all 26 letters of
  406. the alphabet every time!  A huge speed increase for shorter strings!
  407.  
  408. Now back to sorting the word list by length.  I then created indexes into
  409. the sorted word list by length.  For example, the program now easily "knew"
  410. that words of length 7, for example, were in, say, elements 234 through 327
  411. of the candidate word array!  If I'm doing two word anagrams, and my first
  412. word to consider is 5 characters long, and my anagramming string is 11
  413. characters long, I can obviously scan the candidate words that are 6 
  414. characters in length and ignore the rest.  The indexes make that very easy.
  415.  
  416. A change was make to the three-word anagram procedure.  It was faster to
  417. modify the two word anagram procedure a slight bit and concatenate the
  418. first two words being considered and call the modified two word anagram 
  419. procedure with two words consisting of the word1 + word2, and word3.
  420.  
  421. Version 4.00
  422.  
  423. I felt much more comfortable with C at this point, so I decided I'd try
  424. to tackle porting the program.  On April 30, 1993, in one night, I ported
  425. everything except the three word anagrams and a lot of the interactive
  426. options.  The new version was command-line based instead of interactive 
  427. and was more typical of a UNIX-style program.  About a month later, on
  428. May 25, I added the three word anagram code and got the program in a 
  429. fairly stable state so it could be distributed.  Actually, I don't think
  430. I ever took the word "Beta" out of it.  :-)  The program printed its
  431. command line parameters (debug statements).  That was never taken out.
  432. The C version was invoked as follows:
  433.  
  434. wordplay string_to_anagram -123l -f word_file
  435.  
  436. The 1, 2, 3, and l options were obviously the old one, two, three, and word
  437. list flags.  The new option was allowing the user to specify an alternate
  438. word file on the command line.  This program was a straight port, for the 
  439. most part, of the FORTRAN code as far as the anagramming went.  Since I
  440. had a 32-bit C compiler on my 386/33 system, this program ran much faster
  441. than the FORTRAN version using the same algorithms.
  442.  
  443. Version 5.00
  444.  
  445. Sick of being limited to three word anagrams, since C supports recursion,
  446. I added a recursive algorithm that almost makes the old iterative routines
  447. obsolete.  They're still there, though.  The "r" option tells wordplay
  448. to use the recursive algorithm, which basically works as follows 
  449. very much oversimplified).  Read the actual code for details.
  450.  
  451. int recursive_anagram (string, accumulation, level):
  452. {
  453.   if no vowels in string, decrement level, return   (dead end)
  454.   go through list of important candidate words:  for each:
  455.   {
  456.     attempt to extract the word from the string passed into the function.
  457.     if extraction not possible, continue (try next word)
  458.     if extraction was perfect (left no letters), print the anagram
  459.                (accumulation plus the word), then decrement the level, 
  460.                return 
  461.     if extraction was successful, but left some letters, add the word to
  462.                accumulation, increment level, and call the function
  463.                with args (<<whatever is left after extraction>>,
  464.                   accumulation, and level)
  465.   }
  466.   if no extractions were a success, decrement level and return.
  467.   decrement level and return anyway, since we're at the end.  :-)
  468. }
  469.   
  470. Boy, that was a cheesy description.
  471.  
  472. Version 5.01
  473.  
  474. Later during the night of writing 5.00, I realized that I should eliminate
  475. strings of anagrams like "A A ..." or "A I I ... " or "I I ..." or "THE THE .."
  476. because such anagrams are seldom of interest.  Taking them out makes the
  477. program run more quickly and slightly increases the quality of the output.
  478. The check was easy to put into place, so I did it.  I had already given the
  479. code to some friends as 5.00 without that check, so I called this one 5.01.
  480.  
  481. Version 5.02
  482.  
  483. November 30, 1993
  484. Thanksgiving!  While anagramming "Thanksgiving", I found a bug that both 
  485. 5.00 and 5.01 had.  Those versions would occasionally miss some anagrams.  
  486. It had to do with the fact that if there were no words in the word list with 
  487. length equal to the string being passed to the recursive anagram function, no 
  488. words would be used for extraction in the loop.  The bug was fixed my 
  489. adjusting a variable called "max_important length" by decrementing it until
  490. there are some words in the word list having that length or until it reaches
  491. zero, of course.
  492.  
  493. Version 5.10
  494.  
  495. April 11, 1994
  496. Newsgroup alt.anagrams created.  I thought, "I should tell everyone about my
  497. program and make it available".  I didnt have much of a documentation file,
  498. but I posted the information.  In the first 40 hours, after April 9 at 21:00, 
  499. 60 people had taken the code!  I improved the readme file and added the 
  500. minimum and maximum candidate word restriction options back in.  They can
  501. really be of help when anagramming longer strings.
  502.  
  503. Version 5.11
  504.  
  505. On April 14, 1994, I rewrote the "extract" function, and did not really change
  506. the algorithm, but changed from using arrays and subscripts and integer loop
  507. control variables to using pointers and pointer loop control variables.  
  508. Depending on the compiler, this change may or not speed execution of the 
  509. program.  My results so far have ranged from no speed increase to a quite
  510. dramatic 40 percent reduction in execution time, just by using different 
  511. compilers.  
  512.  
  513. Version 5.20
  514.  
  515. April 17, 1994
  516.  
  517. Note:  There was no version 5.12.  It became 5.20 before release.
  518.  
  519. Since the program was ported to C, it only anagrams one string per run.
  520. Storing the entire wordfile into an array is unnecessary and takes a lot 
  521. of memory.  The reason I left it in there so long is because I felt 
  522. eventually I would have the program fixed somehow so it would anagram
  523. more than one string per run.  I changed my mind since anagramming a new
  524. string requires going back through the entire word list to make a new
  525. candidate word list.  The extract routine I originally added in version
  526. 5.00 for use by the recursive anagram procedure, I realized, could be used
  527. to eliminate unnecessary words just as well as the pass1 and pass2 filters
  528. I had been using before.  I made that change and applied it to the words
  529. as they were being read in, directly storing them in the words2 array.
  530. The "words" array was removed from the program.  Now, if MAX_WORDS is set
  531. reasonable low enough, the program might run under MS-DOS without much
  532. work.  The program now starts much more quickly and uses less memory.
  533. It is only storing the candidate words and associated information.
  534.  
  535. Version 5.21
  536.  
  537. April 21, 1994
  538. Bug fixes:
  539.  
  540. Ron Gregory found a simple fencepost error in the one-word iterative
  541. anagramming.  When porting from FORTRAN to C in version 4.00, the error
  542. was made and it was not discovered until a year later!  When using the
  543. indexes into the word list which say "the words of a particular length
  544. are in elements x through y of the candidate word list (the words2 array),
  545. the loop needs to go from x to and including y.  I used a less than instead
  546. of a less than or equal to in the for statement, stopping one short and
  547. missing the last word.  When doing one-word anagrams of a single word, the
  548. last word in the list is usually always that word, so the most obvious one
  549. was getting missed!  Thanks Ron!  I fixed a similar problem in the
  550. recursive algorithm, except it was caused by a decrement of a variable
  551. and a return appearing where I should have had a continue in one of the
  552. cases.
  553.  
  554. However, I discovered a much worse bug, in a way, when I tested the program
  555. with a different wordfile.  I discovered that if the words were lowercase,
  556. it would not be processed correctly.  This problem came in with version
  557. 5.20 with its new read routine.  I apologize for that one.  
  558.  
  559. Yet another bad bug.  Try wordplay aiai -r .  It blows up any wordplay
  560. 5.xx up to 5.20 .  Fewer candidate words than recursive anagramr calls
  561. caused that one.  Solution:  place a #define MAX_ANAGRAM_WORDS at the
  562. top of the program that the user can increase to suit his needs.  Going
  563. deeper than that value crashes the program.  The number of candidate words
  564. was the value previously used, which is too small when anagramming strings
  565. like "catcatcatcatcatcatcatcat" or "aiai".  Another one fixed!
  566.  
  567. Version 5.22
  568.  
  569. April 25, 1994
  570. No bug fixes.  Just a speedup.  If the string returned by exts has 
  571. character zero as the first character, go ahead and continue (try next
  572. word) instead of going through the remaining body of the for loop.  Duh.
  573.  
  574. Version 5.23
  575.  
  576. May 13, 1994
  577. A tiny bug fix.  The statement
  578. while ((wordsn[curpos] == curlen) && (curpos < ncount)) curpos++;
  579. should keep curpos within bounds.  curpos does get incremented to the
  580. value of ncount however, in some cases.  Evaluating wordsn[curpos]
  581. with curpos == ncount could cause a crash, since wordsn is of size
  582. ncount (max valid subscript = ncount - 1).
  583.  
  584. Version 5.24
  585.  
  586. May 16, 1994
  587. Anu Garg at Case Western Reserve University, Cleveland OH found that, on
  588. his machine, if there are no candidate words loaded, the program prints
  589. an error message about malloc() returning NULL.  Evidently, there are some
  590. versions of malloc() that do not like being passed zero as the amount to
  591. allocate and return a NULL pointer if they do.  This has never caused a
  592. problem or an error on any C compiler I've used, but at the end of the
  593. word loading/filtering routine, I now check for zero candidate words and
  594. exit with a different error message.
  595.  
  596. Version 6.00
  597.  
  598. On May 16, 1994, I decided to try a new approach that I previously had
  599. tried to work into the program without much of a speed increase.  This
  600. time, though, I did it more thoroughly.  I made a second copy of the
  601. words2 array (wordss), and sorted the characters of each word 
  602. alphabetically.  I experimented with this idea a long time ago in one
  603. of the old FORTRAN versions, but never went through with it.  I then
  604. sorted this second list in alphabetical order, maintaining a pointer
  605. to the associated words in the words2 array as I sorted.  I then created
  606. indexes into this new array by first letter as I did earlier with lengths in
  607. the words2 array.  I then made some modifications to the recursive procedure 
  608. to take advantage of this new indexing scheme.  It was much faster.  I had
  609. seen several other public domain anagram programs that seemed much faster
  610. then mine, and they seemed to be using an algorithm nearly identical in
  611. functionality to mine.  I finally realized that indexing by least letter
  612. can possible divide the word list into 26 parts, which are each indexed.
  613. Doing it the version 5 way, by length, since the average word length is 
  614. 7 characters, with few words longer than, say, 10 characters, would divide
  615. the word list into only a dozen parts or less in many cases.  Dividing the
  616. list into finer parts was not the only speed increase.  The new algorithm
  617. should not generate all permutations of a given anagram.  This means far
  618. fewer operations.  This was one of the main requests from some of the local
  619. wordplay users, "Can you get rid of the duplicates?".  I am not sure if
  620. 100 percent of the duplicates are removed in all cases, though, but it seems
  621. to get nearly all of them.  It is much faster, also.
  622.  
  623. Version 7.00    5-26-94
  624.  
  625. All redundant permutations are eliminated by not trying any words having 
  626. keys less than the current key we're dealing with at the current level.
  627. Also, the user can now specify whether anagrams containing more than one
  628. occurrence of a given word are acceptable.  This option is more important
  629. now that only one permutation of each anagram is printed.  The recursive
  630. algorithm is now used for everything, and the "1", "2", "3", "o", and "r"
  631. options were removed.  Anagram depth (number of words) is now specified
  632. with the "d" option ("d3" for three words maximum).  The major version 
  633. number was changed because of the command line option changes.  
  634. The "r" is no longer required.  The "x" option is used to turn off 
  635. anagramming, if all you want to do is generate a word list with the "l"
  636. option.  A word may be specified with the "w" option to start anagrams, if
  637. you have a word in mind that you want the anagrams to contain.  Dictionary
  638. words containing apostrophes, hyphens, or other non-alphabetics retain
  639. their internal punctuation when displayed, but only the alphabetic characters
  640. are significant when processing anagrams.  "DON'T" = "DONT", "ONE-EYED" =
  641. "ONEEYED", "KANSAS CITY" = "KANSASCITY", and is processed as a single word.
  642.  
  643. Version 7.01    6-3-94
  644.  
  645. A user at io.com could not get wordplay to work.  It would load the words 
  646. and not generate any anagrams.  The uppercase conversion was not working.
  647. For the toupper macro to work properly on BSD/386 (their operating system),
  648. the ctypes.h file needed to be included.  That's all.  Nothing more.  It
  649. should be included everywhere, though.  BSD/386 is the only environment
  650. where that omission has caused a problem, though.
  651.  
  652. Version 7.02     7-14-94
  653.  
  654. A user, Ulf Lunde, suggested a mode of operation where only the anagrams
  655. are output, with no header, line numbers, or other messages, so they
  656. could be piped or redirected without having to filter anything.  I added
  657. the "s" option to accomplish that.  Adding this option to any command 
  658. simply turns off numbering of output and turns off the header.  If used
  659. with the "l" and "x" options (as in -slx), a word list can be generated 
  660. that is directly useful.  I had thought of this option before, but never
  661. got around to doing it until a user actually requested it.
  662.  
  663. Version 7.10   08-14-94
  664.  
  665. Wordplay was up until now, a serious memory hog!  Now, I've made several
  666. significant changes to reduce the memory usage.  I allocate one contiguous
  667. block to store the words (and another for the keys).  The word block is
  668. reallocated (using realloc()) to increase its size dynamically as more and
  669. more words are read in.  That way, it does not need to be made a "maximum"
  670. size that is big enough to handle the largest dictionary.  It can now start
  671. small and grow as needed.  The vowel index arrays were not being used, so
  672. I deleted them, and their associated code.  The word length indexes (the
  673. ones that say "words of length i are found in positions a through b" are
  674. allocated to be as big as the maximum word length (plus one), instead of
  675. MAX_WORD_LENGTH.
  676.  
  677. After making these changes, I brought the code to an MS-DOS machine and
  678. it compiled under Turbo C ON THE FIRST TRY!!!  It ran fine, unless I tried
  679. to anagram something that required storing too many candidate words.
  680.  
  681. Version 7.11
  682.  
  683. Thanks to Dan Ingalls at Interval Research (interval.com) for a suggestion!
  684. Create integer masks representing the occurring letters in each word:
  685.  
  686. 33222222222211111111110000000000    Bit of integer (tens digit)
  687. 10987654321098765432109876543210    Bit of integer (ones digit)
  688. ......ZYXWVUTSRQPONMLKJIHGFEDCBA    Letter positions  (only 26 bits used)
  689.  
  690. Example:  intmask ("ACE") is 00000000000000000000000000010101 = 21 decimal
  691.  
  692. Before the main loop in anagramr7, the intmask of "s" is computed and 
  693. stored (in "s_mask") and inside the loop, the first thing that is
  694. checked is  "if ((s_mask | wordsmasks[i]) != s_mask)"  skip the rest of
  695. the loop body on this iteration of the loop.
  696.  
  697. Version 7.20
  698.  
  699. Bug fixes and improvements:
  700.  
  701. 1.  The "4" bug.  :-)
  702.     Numbers are no longer treated as punctuation.  Strings with digits
  703.     are no longer considered equivalent to the string without the digits.
  704.     That is, "4th" no longer is equivalent to "th".
  705.  
  706. 2.  Wordplay now is capable of taking the wordlist from stdin by using
  707.     a hyphen as the input file name.  For those of you familiar with
  708.     the "tar" command, it'll come natural to you.  This will allow users
  709.     to break their wordfile into several parts, one with common words,
  710.     one with places, one with people's names, and concatenate the 
  711.     desired ones together and pipe the combination into wordplay.
  712.  
  713. 3.  An option to override the vowel-checking is available.  When this
  714.     option is used, the "anagramr7" routine will continue trying to
  715.     anagram, even when there are no vowels left in the string after
  716.     an extraction.
  717.  
  718. 4.  When the "starting word" specified with the "w" option is an anagram
  719.     of the initial string, that "starting word" is printed as an anagram,
  720.     instead of saying "No anagrams found".
  721.  
  722. ------------------------------------------------------------------------------
  723.  
  724. HAVE FUN !!!
  725.  
  726. ------------------------------------------------------------------------------
  727.  
  728.